package com.google.jstestdriver.maven;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This goal will execute the js-test-driver jar and fails the build on js test failures.
 *
 * @goal test
 */
public class JsTestDriverMojo extends AbstractMojo {
    /**
    * @parameter expression="${arguments}"
    */
    private ArrayList<String> arguments;
    /**
    * @parameter
    */
    private boolean debug;
    /**
    * @parameter
    */
    private boolean skipTests;

    public void execute()
            throws MojoExecutionException {
        if (this.skipTests) {
            getLog().info("Tests are skipped.");
            return;
        }

        processArguments();
        printBanner();
        printArguments();

        Pattern pattern = Pattern.compile("Total ([0-9]+) tests \\(Passed: ([0-9]+); Fails: ([0-9]+); Errors: ([0-9]+)\\).*");
        StringBuffer buffer = new StringBuffer();
        boolean foundStartOfResults = false;
        TestRunnerResults testRunnerResults = null;
        try {
            Process process = ProcessFactory.create(arguments);
            BufferedReader processInputStreamReader = ProcessUtils.getInputStream(process);
            for (String line = processInputStreamReader.readLine(); line != null; line = processInputStreamReader.readLine()) {
                if (this.debug) {
                    System.out.println(line);
                }

                if (line.matches("No browsers were captured.*")) {
                    throw new MojoExecutionException("Unable to capture any browsers");
                }

                Matcher matcher = pattern.matcher(line);
                if (matcher.matches()) {
                    foundStartOfResults = true;
                    testRunnerResults = TestRunnerResults.buildResultsFromMatcher(matcher);
                }

                if (!(foundStartOfResults))
                    continue;
                buffer.append(line).append("\n");
            }

            process.waitFor();
            printResults(buffer, testRunnerResults.hasFailures());
            processResults(testRunnerResults);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage());
        } catch (InterruptedException e) {
            throw new MojoExecutionException(e.getMessage());
        }
    }

    private void processArguments() throws MojoExecutionException {
        String testOutputDir = null;

        for (int i = 0; i < arguments.size(); ++i) {
            String argument = (String) arguments.get(i);
            if ((argument.equals("--testOutput")) && (i + 1 < arguments.size())) {
                testOutputDir = (String) this.arguments.get(i + 1);
            }
        }

        if ((testOutputDir == null) || (testOutputDir.equals("."))) {
            return;
        }
        File directory = new File(testOutputDir);
        if ((directory.exists()) ||
                (directory.mkdirs()))
            return;
        throw new MojoExecutionException("Failed to create testOutput directory");
    }

    private void printResults(StringBuffer buffer, boolean failure) {
        System.out.println(buffer.toString());
    }

    private void processResults(TestRunnerResults testRunnerResults)
            throws MojoExecutionException {

        if ((testRunnerResults != null) && (!(testRunnerResults.hasFailures())))
        {
            System.out.println("All javascript test(s) passed\n");
            return;
        }

        StringBuffer errorString = new StringBuffer();
        errorString.append("Test failure:");
        if (testRunnerResults == null) {
            errorString.append(" unable to parse results");
        } else {
            int errors = testRunnerResults.getTotalTestsErrored();
            int failures = testRunnerResults.getTotalTestsFailed();
            if (errors > 0) {
                errorString.append(" ").append(errors).append(" errors.");
            }
            if (failures > 0) {
                errorString.append(" ").append(failures).append(" failures.");
            }
        }

        throw new MojoExecutionException(errorString.toString());
    }

    private void printArguments() {
        if (!(this.debug))
            return;
        System.out.println("Launching JsTestDriver with options:");
        for (String argument : arguments) {
            System.out.print(argument + " ");
        }
        System.out.println("");
    }

    private void printBanner() {
        System.out.println("\n-----------------------------------------------------\n Started Java Script Unit Testing in JS TEST DRIVER  \n-----------------------------------------------------\n");
    }

}
